package zydhjwt

import (
	"errors"
	"github.com/golang-jwt/jwt/v4"
	"time"
)

//获取jwt
//    go get -u github.com/golang-jwt/jwt/v4

// jwtSecret 用于签名的字符串
var jwtSecret = []byte("$2a$12$HOJOi/82BHqN4ig3.UufnOh2S9pOxZpXiv.vQ/F3mjCnzC7COZVoA")

// TokenExpireDuration  定义JWT的过期时间，这里以24小时为例：
const TokenExpireDuration = time.Hour * 24

//----------------------------------------自定义Claims----------------------------------------

//我们需要定制自己的需求来决定JWT中保存哪些数据，比如我们规定在JWT中要存储username信息，那么我们就定义一个Claims结构体如下：

// Claims 自定义声明类型 并内嵌jwt.RegisteredClaims
// jwt包自带的jwt.RegisteredClaims只包含了官方字段
// 假设我们这里需要额外记录一个username字段，所以要自定义结构体
// 如果想要保存更多信息，都可以添加到这个结构体中
type Claims struct {
	// 可根据需要自行添加字段
	ID                   uint   `json:"id"`
	UserName             string `json:"user_name"`
	Authority            int    `json:"authority"`
	jwt.RegisteredClaims        // 内嵌标准的声明
}

// GenerateToken 签发 token
func GenerateToken(id uint, userName string, authority int) (string, error) {
	// 创建一个我们自己的声明
	claims := Claims{
		ID:        id,
		UserName:  userName, // 自定义字段
		Authority: authority,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
			Issuer:    "zydh", // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象 使用hash256加密
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	//签名  // 使用指定的secret签名并获得完整的编码后的字符串token
	token, err := tokenClaims.SignedString(jwtSecret)
	return token, err
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*Claims, error) {
	// 解析token
	// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (i interface{}, err error) {
		// 直接使用标准的Claim则可以直接使用Parse方法
		//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {
		return jwtSecret, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, errors.New("that's not even a token")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("token is expired")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, errors.New("token not active yet")
			} else {
				return nil, errors.New("couldn't handle this token")
			}
		}
	}
	// 对token对象中的Claim进行类型断言
	if claims, ok := token.Claims.(*Claims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

// EmailClaims 自定义邮箱token
type EmailClaims struct {
	// 可根据需要自行添加字段
	UserID               uint   `json:"user_id"`
	Email                string `json:"email"`
	Password             string `json:"password"`
	Scene                uint   `json:"scene"`
	jwt.RegisteredClaims        // 内嵌标准的声明
}

// GenerateEmailToken 签发 email token
func GenerateEmailToken(userId uint, email, password string, scene uint) (string, error) {
	// 创建一个我们自己的声明
	claims := EmailClaims{
		UserID:   userId,
		Email:    email,    // 自定义字段
		Password: password, // 自定义字段
		Scene:    scene,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
			Issuer:    "zydh", // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象 使用hash256加密
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	//签名  // 使用指定的secret签名并获得完整的编码后的字符串token
	token, err := tokenClaims.SignedString(jwtSecret)
	return token, err
}

// ParseEmailToken 解析email token JWT
func ParseEmailToken(tokenString string) (*EmailClaims, error) {
	// 解析token
	// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法
	token, err := jwt.ParseWithClaims(tokenString, &EmailClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		// 直接使用标准的Claim则可以直接使用Parse方法
		//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {
		return jwtSecret, nil
	})
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, errors.New("that's not even a token")
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return nil, errors.New("token is expired")
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, errors.New("token not active yet")
			} else {
				return nil, errors.New("couldn't handle this token")
			}
		}
	}
	// 对token对象中的Claim进行类型断言
	if claims, ok := token.Claims.(*EmailClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}
